package pl.enigmatic.parser.xml;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;

import org.w3c.dom.Node;

import pl.enigmatic.util.Reflection;
import pl.enigmatic.parser.XMLAlias;
import pl.enigmatic.parser.XMLConverter;
import pl.enigmatic.parser.XMLParser;
import pl.enigmatic.parser.string.Converter;


public class PropertyParser extends BasicPropertyParser<PropertyParser.Setter> {

	protected static class Setter extends pl.enigmatic.parser.xml.BasicPropertyParser.Accessor {

		@SuppressWarnings("unchecked")
		private static Parser<?> createParser(final Field f, final Method m, final Converter<?> c) {
			final Class<?> type = m.getParameterTypes()[0];
			Parser<?> parser = null;
			if (m.isAnnotationPresent(XMLParser.class)) {
				parser = Parser.createCustomParser((Class<Parser<?>>) m.getAnnotation(XMLParser.class).value());
			} else if (f != null && f.isAnnotationPresent(XMLParser.class)) {
				parser = Parser.createCustomParser((Class<Parser<?>>) f.getAnnotation(XMLParser.class).value());
			}
			if (parser == null && type != null) {
				parser = Parser.createParser(type);
			}
			return parser != null ? parser.copy(c) : null;
		}

		private final Method method;

		Setter(final String name, final Field f, final Method m, final Converter<?> c) {
			super(name, m.getParameterTypes()[0], createParser(f, m, c));
			method = m;
		}

		@Override
		public boolean access(final Object target, final Object value, final ParsingInfo<?> info) {
			try {
				method.invoke(target, value);
				return true;
			} catch (final Exception e) {
				e.printStackTrace();
			}
			return false;
		}
	}

	PropertyParser(final Parser<?> owner) {
		super(owner, true);
		final Class<?> clazz = owner.getClazz();
		final Map<String, Method> map = Reflection.getDeclaredSetters(clazz);
		for (final Entry<String, Method> m : map.entrySet()) {
			Converter<?> c = null;
			Field f = null;
			final Method mtd = m.getValue();
			final String property = m.getKey();
			if (mtd.isAnnotationPresent(XMLConverter.class)) {
				c = Converter.create(mtd.getAnnotation(XMLConverter.class).value());
			} else {
				try {
					f = clazz.getDeclaredField(property);
					if (f != null && f.isAnnotationPresent(XMLConverter.class)) {
						c = Converter.create(f.getAnnotation(XMLConverter.class).value());
					}
				} catch (final Exception e) {}
			}
			accessors.put(property, new Setter(property, f, m.getValue(), c));
			if (mtd.isAnnotationPresent(XMLAlias.class)) {
				accessors.addAliases(property, mtd.getAnnotation(XMLAlias.class).value());
			}
			if (f != null && f.isAnnotationPresent(XMLAlias.class)) {
				accessors.addAliases(property, f.getAnnotation(XMLAlias.class).value());
			}
		}
	}

	@Override
	public NodeNameInfo parseNodeName(final Node node, final ParsingInfo<?> info) {
		NodeNameInfo name = null;
		final String property = getPropertyName(node, owner);
		if (accessors.containsKey(property)) {
			final String clazz = NodeNameInfo.getFullClassName(node, info.getStaticInfo());
			if (clazz != null) {
				name = new NodeNameInfo(property, clazz, Parser.createParser(clazz));
			} else {
				final Setter s = accessors.get(property);
				name = new NodeNameInfo(property, s.getType(), s.getParser());
			}
		}
		return name;
	}
}
